#pragma once

#include <iostream>
#include <string>
#include <semaphore.h>
#include <pthread.h>
#include <vector>

template <typename T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem); // 获取信号量，获取成功时，信号量的值减少，有一个资源被占用
    }
    void V(sem_t &sem)
    {
        sem_post(&sem); // 增加信号量
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

public:
    RingQueue(int cap)
        : _cap(cap), _consumer_step(0), _productor_step(0), _ring_queue(cap)
    {
        sem_init(&_room_sem, 0, _cap); // 空间的信号量初始值为 _cap
        sem_init(&_data_sem, 0, 0);

        pthread_mutex_init(&_consumer_mutex, nullptr);
        pthread_mutex_init(&_productor_mutex, nullptr);
    }
    void Enqueue(const T &in)
    {
        P(_room_sem); // 入环形队列时，先申请空间信号量，占空间
        Lock(_productor_mutex);//因为 _productor_step 是临界资源，需要加锁保护临界资源

        // 既拿到锁了，也有空间，就可以生产
        _ring_queue[_productor_step++] = in; // 生产
        _productor_step %= _cap;

        Unlock(_productor_mutex);
        V(_data_sem); // 放入一个数据，数据信号量增加
    }
    void Pop(T *out)
    {
        P(_data_sem);
        Lock(_consumer_mutex);//因为 _consumer_step 是临界资源，需要加锁保护临界资源

        *out = _ring_queue[_consumer_step++];
        _consumer_step %= _cap;

        Unlock(_consumer_mutex);
        V(_room_sem); // 空出一个空间
    }
    ~RingQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_room_sem);

        pthread_mutex_destroy(&_consumer_mutex);
        pthread_mutex_destroy(&_productor_mutex);
    }

private:
    std::vector<T> _ring_queue; // 用数组模拟环形队列
    int _cap;                   // 环形队列的上限

    // 访问队列的下标
    int _consumer_step;
    int _productor_step;

    // 信号量
    sem_t _room_sem;
    sem_t _data_sem;

    // 互斥锁
    pthread_mutex_t _consumer_mutex;
    pthread_mutex_t _productor_mutex;
};